# $Id: $

module Visitor::Combinator::All;
# extends Visitor::Combinator::Defined;

_ONLOAD();

sub _ONLOAD() {
	if our $onload_done { return 0; }
	$onload_done := 1;
		
	Parrot::IMPORT('Dumper', 'ASSERT DIE DUMP DUMP_ NOTE');
	
	my $class_name := 'Visitor::Combinator::All';
	
	NOTE("Creating class ", $class_name);
	Class::SUBCLASS($class_name,
		'Visitor::Combinator');
	
	Class::multi_method($class_name, 'visit', :starting_with('_visit_'));
	
	NOTE("done");
}

method do_visit($node) {
	my $result := self.payload.visit($node);
	self.success(self.payload.success);
	return $result;
}

method init(@children, %attributes) {
	if +@children {
		self.payload(@children.shift);
	}
}

method payload(*@value)		{ self._ATTR('payload', @value); }

method visit_attr($node, $attr_name) {
	NOTE("Visiting attribute: '", $attr_name, "'");
	my $value	:= Class::call_method($node, $attr_name);

	if Parrot::defined($value) {
		my $original	:= $value;
		DUMP($original);
		
		$value		:= self.do_visit($value);
	
		if self.success && ! ($original =:= $value) {
			NOTE("Replacing with new node.");
			DUMP($value);
			
			Class::call_method($node, $attr_name, $value);
		}
	}
	
	return $value;
}

method visit_children($node) {
	my $index := 0;
	my $num_children := +@($node);
	
	NOTE("Visiting ", $num_children, " children of node: ", $node);
	
	while self.success && $index < $num_children {
		my $result := self.do_visit($node[$index]);
		
		if self.success {
			$node[$index] := $result;
		}

		$index++;
	}
	
	return $node;
}

method visit_default($node) {
	my $class_name := Class::name_of($node);
	NOTE("Visiting ", $class_name, " node: ", $node);
	
	DIE("Unanticipated node type: ", $class_name, 
		". Do you need to re-run class_hierarchy.pl?");
}

method visit_noop($node) {
	NOTE("No children, no attributes. Nothing to do.");
	self.PASS;
	return $node;
}

method visit_type_decl($node) {
	self.PASS;

	if $node.nominal {
		NOTE("Visiting nominal type");
		self.visit_attr($node, 'nominal');
	}

	if $node.definition {
		# Only applies to 'function returning,' so far. Might also
		# apply to CUES types.
	
		NOTE("Visiting definition");	
		self.visit_attr($node, 'definition');
	}

	return $node;
}

# The class_hierarchy script will not emit method definitions for methods
# that appear outside of the generated section, below. In order to add
# traversal logic, move the generated method down below the generated
# section, and edit appropriately. 
#
# And yeah, there should be like nothing in the generated section. For 
# *this* class, everything either has children, or calls visit_noop($node).
# The visit_default method is a landmine to make things blow up when
# a new class joins the hierarchy and I forget to update stuff. (Happens
# a lot.)

# class_hierarchy.pl BEGIN CONTENTS self.visit_default($node);

# This section automatically generated by austin at Tue Oct 20 01:55:22 2009 GMT

method _visit_Slam_Adverb($node)		{ self.visit_default($node); }
method _visit_Slam_Adverb_Flat($node)		{ self.visit_default($node); }
method _visit_Slam_Adverb_Multi($node)		{ self.visit_default($node); }
method _visit_Slam_Adverb_Named($node)		{ self.visit_default($node); }
method _visit_Slam_Adverb_Optional($node)	{ self.visit_default($node); }
method _visit_Slam_Adverb_RegisterClass($node)	{ self.visit_default($node); }
method _visit_Slam_Adverb_Slurpy($node)		{ self.visit_default($node); }
method _visit_Slam_Adverb_Vtable($node)		{ self.visit_default($node); }
method _visit_Slam_IncludeFile($node)		{ self.visit_default($node); }
method _visit_Slam_Lookups($node)		{ self.visit_default($node); }
method _visit_Slam_Mixin_HasType($node)		{ self.visit_default($node); }
method _visit_Slam_Scope_GlobalRoot($node)	{ self.visit_default($node); }
method _visit_Slam_Scope_HllRoot($node)		{ self.visit_default($node); }
method _visit_Slam_Scope_Namespace($node)	{ self.visit_default($node); }
method _visit_Slam_Symbol_Namespace($node)	{ self.visit_default($node); }
method _visit_Slam_Type_Declarator($node)	{ self.visit_default($node); }

# class_hierarchy.pl END

# The above "END" directive marks the end of the generated section. The
# methods below are hand-edited.

method _visit_Slam_Error($node)		{ self.visit_noop($node); }
method _visit_Slam_Literal_Float($node)	{ self.visit_noop($node); }
method _visit_Slam_Literal_Integer($node)	{ self.visit_noop($node); }
method _visit_Slam_Literal_String($node)	{ self.visit_noop($node); }
method _visit_Slam_Message($node)		{ self.visit_noop($node); }

method _visit_Slam_Scope_Function($node) {
	NOTE("Visiting function scope: ", $node);
	
	self.PASS;
	
	if $node.parameter_scope {
		NOTE("Visiting parameter_scope of ", $node);	
		self.visit_attr($node, 'parameter_scope');
	}

	NOTE("Visiting child nodes of ", $node);
	my $func_scope := self.visit_children($node);
	
	return $func_scope;
}

method _visit_Slam_Scope_Local($node) {
	self.PASS;
	
	my $local := self.visit_children($node);
	# NB: Not visiting "using-namespaces"
	return $local;
}

method _visit_Slam_Scope_NamespaceDefinition($node) { 
	self.PASS;
	return self.visit_children($node); 
}

method _visit_Slam_Scope_Parameter($node) {
	self.PASS;
	
	my $params := self.visit_children($node);
	return $params;
}

method _visit_Slam_Scope_Pervasive($node)	{ DIE("Why are you here?"); }
method _visit_Slam_Statement_Null($node)	{ self.visit_noop($node); }

method _visit_Slam_Statement_Return($node) {
	self.PASS;
	
	NOTE("Visiting child node");
	my $result := self.visit_children($node);
	return $result;
}

method _visit_Slam_Statement_SymbolDeclarationList($node) {
	self.PASS;
	NOTE("Visiting child nodes");
	my $result := self.visit_children($node);
	return $result;
}

method _visit_Slam_Statement_UsingNamespace($node) { self.visit_noop($node); }
method _visit_Slam_Stmts($node)		{ self.visit_children($node); }

method _visit_Slam_Symbol_Declaration($node) {
	self.PASS;
	
	NOTE("Visiting declaration of symbol ", $node);

	if $node.type {
		NOTE("Visiting type");
		self.visit_attr($node, 'type');
	}
	
	if $node.initializer {
		NOTE("Visiting initializer");
		self.visit_attr($node, 'initializer');
	}
	
	return $node;
}

method _visit_Slam_Symbol_Reference($node)	{ self.visit_noop($node); }

method _visit_Slam_Type_Array($node)		{ self.visit_type_decl($node); }
method _visit_Slam_Type_Function($node)	{ self.visit_type_decl($node); }
method _visit_Slam_Type_Hash($node)		{ self.visit_type_decl($node); }
method _visit_Slam_Type_MultiSub($node)	{ self.visit_type_decl($node); }
method _visit_Slam_Type_Pointer($node)	{ self.visit_type_decl($node); }
method _visit_Slam_Type_Specifier($node)	{ self.visit_noop($node); }
method _visit_Slam_Warning($node)		{ self.visit_noop($node); }
